import * as THREE from 'three'
import { UUIDv4 } from '../../gongJu/gongJu.js'
import { wuTiCaoZuo } from '../../wuLi/WuLiWorker.js'

export class YueShu {
	bianHao = UUIDv4()
	mingZi = ''
	moXing = null
	shiYueShu = true
	yueShuLeiXing = null
	yueShuCanShu = null
	yueShuFanWei = null // 约束范围
	jiaZaiZhuangTai = false
	jinZhiPengZhuang = false // 连接物体间禁止碰撞
	
	static quanBuYueShu = [] // 已加载的全部约束
	
	/**
	 * @param {String} yueShuLeiXing 约束类型
	 * @param {Object} yueShuCanShu 约束参数
	 * @param {GangTi} yueShuCanShu.wuTiA 约束的物体A
	 * @param {GangTi} yueShuCanShu.wuTiB 约束的物体B
	 * @param {Vector3} yueShuCanShu.weiZhiA 在物体A的局部坐标
	 * @param {Vector3} yueShuCanShu.weiZhiB 在物体B的局部坐标
	 * @param {Vector3} yueShuCanShu.xuanZhuanA 在世界坐标系物体A的局部旋转
	 * @param {Vector3} yueShuCanShu.xuanZhuanB 在世界坐标物体B的局部旋转
	 * @param {Vector3} yueShuCanShu.zuoBiaoZhouA 在物体A的局部坐标轴
	 * @param {Vector3} yueShuCanShu.zuoBiaoZhouB 在物体B的局部坐标轴
	 */
	constructor(yueShuLeiXing, yueShuCanShu, jinZhiPengZhuang = false) {
		this.yueShuLeiXing = yueShuLeiXing
		this.jinZhiPengZhuang = jinZhiPengZhuang
		this.yueShuCanShu = yueShuCanShu
		this.moXing = this.shengChengMoXing()
		this.moXing.traverse(wangGe => wangGe.userData.huoQuYueShu = () => this)
		// 物理世界caoKaoA = true时模型加进物体A
		let { wuTiA, wuTiB, weiZhiA, xuanZhuanA, zuoBiaoZhouA } = this.yueShuCanShu
		this.moXing.position.copy(weiZhiA)
		wuTiA.yueShu.add(this)

		if (wuTiB) wuTiB.yueShu.add(this)
		if (xuanZhuanA) this.moXing.quaternion.copy(xuanZhuanA)
		if (zuoBiaoZhouA) {
			this.moXing.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), zuoBiaoZhouA)
			Reflect.deleteProperty(this.yueShuCanShu, 'zuoBiaoZhouA')
			Reflect.deleteProperty(this.yueShuCanShu, 'zuoBiaoZhouB')
		}

		this.moXing.visible = false
	}
	// 生成模型
	shengChengMoXing() {
		let moXing = null
		
		if (this.yueShuLeiXing == '铰链') {
			const material = new THREE.MeshBasicMaterial({ color: 0x00aaff, depthTest: false })
			const material1 = new THREE.MeshBasicMaterial({ color: 0x0099ee, depthTest: false })
			moXing = new THREE.Mesh( new THREE.ExtrudeGeometry(), [material, material1])
		} else if (this.yueShuLeiXing == '滑块') {
			const material = new THREE.MeshBasicMaterial({ color: 0xff5500, depthTest: false })
			const material1 = new THREE.MeshBasicMaterial({ color: 0xdd3300, depthTest: false })
			moXing = new THREE.Mesh( new THREE.ExtrudeGeometry(), [material, material1])
		} else {
			const material = new THREE.MeshNormalMaterial({ depthTest: false })
			moXing = new THREE.Mesh( new THREE.BoxGeometry( 0.05, 0.05, 0.05 ), material)
			moXing.visible = false
		}
		
		let zuoBiaoZhou = shengChengZuoBiaoZhou()
		moXing.add( zuoBiaoZhou );
		
		this.gengXinJiHeTi(moXing)
		
		return moXing
	}
	// 根据约束范围更新几何体
	gengXinJiHeTi(moXing) {
		moXing.geometry.dispose()
		
		let geometry = null
		
		if (this.yueShuLeiXing == '铰链') {
			let xuanZhuanFanWei = this.yueShuFanWei ?? { zuiXiao: 0, zuiDa: Math.PI * 1.999 }
			let huDu = xuanZhuanFanWei.zuiDa - xuanZhuanFanWei.zuiXiao // 弧度
			
			let xuanZhuan = (x, y) => {
				let vec2 = new THREE.Vector2(x, y)
				vec2.rotateAround(new THREE.Vector2(0, 0), huDu)
				return vec2.toArray()
			}
			
			const shape = new THREE.Shape();
			shape.moveTo(0, 0);
			shape.absarc(0, 0, 0.3, 0, huDu, false)
			shape.lineTo(...xuanZhuan(0.35, 0))
			shape.lineTo(...xuanZhuan(0.275, 0.1))
			shape.lineTo(...xuanZhuan(0.2, 0))
			shape.absarc(0, 0, 0.25, huDu, 0, true)
			
			const extrudeSettings = { steps: 1, depth: 0.05, bevelEnabled: false }; // depth正负为z轴方向
			geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
		} else if (this.yueShuLeiXing == '滑块') {
			let pingYiFanWei = this.yueShuFanWei ?? { zuiXiao: 0, zuiDa: 1 }
			let chang = pingYiFanWei.zuiDa - pingYiFanWei.zuiXiao
			
			const shape = new THREE.Shape();
			shape.moveTo(0, 0);
			shape.lineTo(0, 0.03)
			shape.lineTo(chang, 0.03)
			shape.lineTo(chang, 0.1)
			shape.lineTo(chang + 0.15, 0)
			shape.lineTo(chang, -0.1)
			shape.lineTo(chang, -0.03)
			shape.lineTo(0, -0.03)
			
			const extrudeSettings = { steps: 1, depth: 0.05, bevelEnabled: false }; // depth正负为z轴方向
			geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
		} else {
			geometry = new THREE.BoxGeometry( 0.05, 0.05, 0.05 );
		}
		
		moXing.geometry = geometry
	}
	// 根据约束模型在物体A中的状态，更新约束参数
	gengXinYueShuCanShu() {
		let { wuTiA, wuTiB } = this.yueShuCanShu
		if (!wuTiA?.jiaZaiZhuangTai || !wuTiB?.jiaZaiZhuangTai) return
		
		let fuZhu = this.moXing.clone()
		// 由于克隆不包括parent，从原模型获取世界坐标和旋转
		this.moXing.getWorldPosition(fuZhu.position)
		this.moXing.getWorldQuaternion(fuZhu.quaternion)

		wuTiA.moXing.attach(fuZhu)
		let weiZhiA = fuZhu.position.clone()
		let xuanZhuanA = fuZhu.quaternion.clone()
		wuTiB.moXing.attach(fuZhu)
		let weiZhiB = fuZhu.position.clone()
		let xuanZhuanB = fuZhu.quaternion.clone()
		
		Object.assign(this.yueShuCanShu, { wuTiA, wuTiB, weiZhiA, weiZhiB, xuanZhuanA, xuanZhuanB })

		fuZhu.removeFromParent()
	}
	
	tianJiaYueShu() {
		let { wuTiA, wuTiB, weiZhiA, weiZhiB, xuanZhuanA, xuanZhuanB, zuoBiaoZhouA, zuoBiaoZhouB } = this.yueShuCanShu
		
		let caoZuo = {
			mingZi: 'tianJiaYueShu',
			canShu: {
				yueShuLeiXing: this.yueShuLeiXing,
				jinZhiPengZhuang: this.jinZhiPengZhuang,
				bianHao: this.bianHao,
				wuTiA: wuTiA.bianHao,
				wuTiB: wuTiB?.bianHao,
				weiZhiA: weiZhiA.toArray(),
				weiZhiB: weiZhiB?.toArray(),
				xuanZhuanA: xuanZhuanA?.toArray(),
				xuanZhuanB: xuanZhuanB?.toArray(),
				zuoBiaoZhouA: zuoBiaoZhouA?.toArray(),
				zuoBiaoZhouB: zuoBiaoZhouB?.toArray()
			}
		}
		
		wuTiCaoZuo[this.bianHao] ??= []
		wuTiCaoZuo[this.bianHao].push(caoZuo)
	}
	/**
	 * @param {Object} sheZhiCanShu
	 * @param {Boolean} sheZhiCanShu.suoDing
	 * @param {Object} sheZhiCanShu.jiaoLian
	 * @param {Object} sheZhiCanShu.jiaoLian.xuanZhuanFanWei
	 * @param {Number} sheZhiCanShu.jiaoLian.xuanZhuanFanWei.zuiXiao
	 * @param {Number} sheZhiCanShu.jiaoLian.xuanZhuanFanWei.zuiDa
	 * @param {Number} sheZhiCanShu.jiaoLian.xuanZhuanSuDu
	 * @param {Object} sheZhiCanShu.huaKuai
	 * @param {Object} sheZhiCanShu.huaKuai.pingYiFanWei
	 * @param {Number} sheZhiCanShu.huaKuai.pingYiFanWei.zuiXiao
	 * @param {Number} sheZhiCanShu.huaKuai.pingYiFanWei.zuiDa
	 * @param {Number} sheZhiCanShu.huaKuai.pingYiSuDu
	 * @param {Object} sheZhiCanShu.dian
	 * @param {Vector3} sheZhiCanShu.dian.weiZhiA
	 * @param {Vector3} sheZhiCanShu.dian.weiZhiB
	 */
	sheZhiYueShu(sheZhiCanShu) {
		if (!this.jiaZaiZhuangTai) return
		let caoZuoShuZu = wuTiCaoZuo[this.bianHao] ??= []
		
		if (sheZhiCanShu.jiaoLian?.xuanZhuanFanWei) this.yueShuFanWei = sheZhiCanShu.jiaoLian.xuanZhuanFanWei
		if (sheZhiCanShu.huaKuai?.pingYiFanWei) this.yueShuFanWei = sheZhiCanShu.huaKuai.pingYiFanWei
		
		// 数据转数组
		if (sheZhiCanShu.dian) {
			sheZhiCanShu.dian.weiZhiA &&= sheZhiCanShu.dian.weiZhiA.toArray()
			sheZhiCanShu.dian.weiZhiB &&= sheZhiCanShu.dian.weiZhiB.toArray()
		}
		
		if (caoZuoShuZu) {
			let sheZhiCaoZuo = caoZuoShuZu.find(caoZuo => caoZuo.mingZi == 'sheZhiYueShu')
			if (sheZhiCaoZuo) {
				let canShu = sheZhiCaoZuo.canShu
				canShu.bianHao = this.bianHao
				canShu.mingZi = this.mingZi
				canShu.suoDing = Boolean(sheZhiCanShu.suoDing)

				if (sheZhiCanShu.jiaoLian) canShu.jiaoLian = sheZhiCanShu.jiaoLian
				if (sheZhiCanShu.huaKuai) canShu.huaKuai = sheZhiCanShu.huaKuai
				if (sheZhiCanShu.dian) canShu.dian = sheZhiCanShu.dian
			} else {
				let caoZuo = {
					mingZi: 'sheZhiYueShu',
					canShu: {
						bianHao: this.bianHao,
						mingZi: this.mingZi,
						suoDing: sheZhiCanShu.suoDing,
						jiaoLian: sheZhiCanShu.jiaoLian,
						huaKuai: sheZhiCanShu.huaKuai,
						dian: sheZhiCanShu.dian
					}
				}
				
				wuTiCaoZuo[this.bianHao].push(caoZuo)
			}
		}
	}
	
	yiChuYueShu() {
		let caoZuo = { mingZi: 'yiChuYueShu', canShu: { bianHao: this.bianHao } }
		wuTiCaoZuo[this.bianHao] ??= []
		wuTiCaoZuo[this.bianHao].push(caoZuo)
	}
	
	jiaZai() {
		if (this.jiaZaiZhuangTai) return
		if (!this.yueShuCanShu) return
		
		let  { wuTiA, wuTiB } = this.yueShuCanShu
		
		let chuLi = () => {
			this.jiaZaiZhuangTai = true
			wuTiA.moXing.add(this.moXing)
			// wuTiA，wuTiB已加载时更新约束参数
			this.gengXinYueShuCanShu()
			this.tianJiaYueShu()
			YueShu.quanBuYueShu.push(this)
		}
		
		if (this.yueShuLeiXing == '点' && wuTiA.jiaZaiZhuangTai && !wuTiB) chuLi() // 点约束 拾取
		else if (wuTiA.jiaZaiZhuangTai && wuTiB.jiaZaiZhuangTai) chuLi()
	}
	
	xieZai() {
		this.jiaZaiZhuangTai = false
		
		this.moXing.removeFromParent()
		this.yiChuYueShu()
		
		let suoYin = YueShu.quanBuYueShu.findIndex(w => w == this)
		if (suoYin > -1) YueShu.quanBuYueShu.splice(suoYin, 1)
	}
	
	xiaoHui() {
		this.xieZai()
		
		this.yueShuCanShu.wuTiA.yueShu.delete(this)
		this.yueShuCanShu.wuTiB?.yueShu.delete(this)
		
		function xh(a) {
			a.geometry?.dispose()
			if (Array.isArray(a.material)) {
				a.material.forEach(b => {
					b.map?.dispose()
					b.dispose()
				})
			} else {
				a.material?.map?.dispose()
				a.material?.dispose()
			}
		}
		this.moXing.traverse(xh)
	}
}

function shengChengZuoBiaoZhou() {
	// 坐标轴
	let zhouChang = 0.3 // 轴长
	let zuoBiaoZhou = new THREE.InstancedMesh( new THREE.CylinderGeometry( 0.01, 0.01, zhouChang, 8 ), new THREE.MeshBasicMaterial({ depthTest: false, transparent: true, opacity: 0.8 }), 3 );
	let vec = new THREE.Vector3()
	let mat = new THREE.Matrix4()
	let zhouChang_2 = zhouChang / 2
	let PI_2 = Math.PI / 2
	// x
	zuoBiaoZhou.setColorAt(0, new THREE.Color(0xff3300))
	mat.makeRotationZ(PI_2)
	vec.set(zhouChang_2, 0, 0)
	mat.setPosition(vec)
	zuoBiaoZhou.setMatrixAt(0, mat)
	mat.identity()
	// y
	zuoBiaoZhou.setColorAt(1, new THREE.Color(0x00ff33))
	vec.set(0, zhouChang_2, 0)
	mat.setPosition(vec)
	zuoBiaoZhou.setMatrixAt(1, mat)
	mat.identity()
	// z
	zuoBiaoZhou.setColorAt(2, new THREE.Color(0x0033ff))
	mat.makeRotationX(PI_2)
	vec.set(0, 0, zhouChang_2)
	mat.setPosition(vec)
	zuoBiaoZhou.setMatrixAt(2, mat)
	
	zuoBiaoZhou.instanceMatrix.needsUpdate = true;
	zuoBiaoZhou.instanceColor.needsUpdate = true;
	
	return zuoBiaoZhou
}